/*
 ------------------------------------------------------------------------------
 Copyright (C) 1996-2001 Id Software, Inc.

 This file is part of the Quake source code.

 The Quake source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quake source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quake source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// sg_local.h - local header to all server-game files
//


#ifndef __SG_LOCAL_H__
#define __SG_LOCAL_H__


#include "../game.h"
#include "sg_public.h"


class	etClient;
class	etEntity;


/*
 ==============================================================================

 CLIENTS

 ==============================================================================
*/

#define MAX_VOICE_MESSAGES				128

enum clientConnected_t {
	CON_DISCONNECTED,
	CON_CONNECTING,
	CON_CONNECTED
};

enum weaponState_t {
	WEAPON_READY,
	WEAPON_RAISING,
	WEAPON_DROPPING,
	WEAPON_FIRING,
};

struct voiceMessage_t {
	int						clientNum;

	const void *			data;
	int						size;
};

struct snapshot_t {
	int						sequence;

	playerState_t			playerState;

	int						numEntities;
	entityState_t			entityStates[MAX_SNAPSHOT_ENTITIES];
};

class etClient {
public:
	int						number;
	bool					isLocal;

	clientConnected_t		connected;

	char					playerName[MAX_PLAYER_NAME_LENGTH];
	int						voiceChat;

	etEntity *				entity;				// Player entity

	playerState_t			state;

	etPhysicsPlayer			physics;

	bool					spectator;

	bool					godMode;
	bool					noClip;
	bool					noTarget;

	int						ping;

	userCmd_t				lastUserCmd;		// Last user command executed

	int						oldCmdFlags;		// User command flags from previous think
	int						oldCmdButtons;		// User command buttons from previous think

	int						numVoiceMessages;
	voiceMessage_t			voiceMessages[MAX_VOICE_MESSAGES];

	snapshot_t *			snapshots[PACKET_BACKUP];

	int						startFrame;
	int						endFrame;
	int						frameTime;

private:
	bool					CheatsOK () const;

	void					CmdGod ();
	void					CmdNoClip ();
	void					CmdNoTarget ();
	void					CmdGive ();
	void					CmdKill ();
	void					CmdSay () const;
	void					CmdSayTeam () const;
	void					CmdGetViewPos () const;
	void					CmdSetViewPos ();

	void					UpdateWeaponState ();

	void					AxeWeapon ();
	void					ShotgunWeapon ();
	void					SuperShotgunWeapon ();
	void					NailgunWeapon ();
	void					SuperNailgunWeapon ();
	void					GrenadeLauncherWeapon ();
	void					RocketLauncherWeapon ();
	void					LightningGunWeapon ();

public:
	void					Command ();

	void					UserInfoChanged (char *userInfo);

	const char *			Connect (int clientNum, bool localClient, const char *ip, char *userInfo, bool firstTime);
	void					Disconnect ();

	void					Begin ();
	void					Spawn ();

	void					Pain (etEntity *inflictor, etEntity *attacker, const glqVec3 &point, const glqVec3 &direction, int damage);
	void					Kill (etEntity *inflictor, etEntity *attacker, const glqVec3 &point, const glqVec3 &direction, int damage, meansOfDeath_t meansOfDeath);


	void					Think (const userCmd_t *userCmd);

	void					SelectWeapon ();
	void					DropWeapon ();
	void					FireWeapon ();
	void					RunWeapon ();

	void					Teleport (const glqVec3 &origin, const glqAngles &angles, bool keepMomentum = false);

	void					SetViewAngles (const glqAngles &angles);

	glqVec3					GetViewOrigin () const;
	glqAngles				GetViewAngles () const;
	glqMat3					GetViewAxis () const;

	void					WriteGameState (msg_t *msg, int offset);
	void					WriteSnapshot (msg_t *msg, int sequence, int deltaMessage, bool rateDelayed);

	void					SetPing (int ping);

	void					AddEvent (int eventType, int eventParm);
};

/*
 ==============================================================================

 ENTITIES

 ==============================================================================
*/

#define MAX_ENTITY_NAME_LENGTH			64

#define DAMAGE_KILL						999999

#define DEFAULT_AUDIBLERANGE			2500

enum gameEntityFlags_t {
	FL_NOCLIENT				= BIT(0),			// Don't send to clients
	FL_SINGLECLIENT			= BIT(1),			// Only send to a single client
	FL_NOTSINGLECLIENT		= BIT(2),			// Send to everyone but one client
	FL_BROADCAST			= BIT(3),			// Send to all connected clients
	FL_PORTAL				= BIT(4),			// Also send all entities visible from target's position
	FL_REMOTE				= BIT(5),			// Also send all entities visible from target's position
	FL_VISIBLE				= BIT(6),			// Check if visible before sending to clients
	FL_AUDIBLE				= BIT(7),			// Check if audible before sending to clients
	FL_HIDDEN				= BIT(8),			// Don't send to clients and don't clip against it
	FL_PHYSICS				= BIT(9),			// Run physics and update position and orientation
	FL_PUSHER				= BIT(10),			// Try pushing other entities
	FL_CRUSHER				= BIT(11),			// Crush any blocking entities
	FL_CONVEYOR				= BIT(12),			// Move entities standing on this entity
	FL_ENABLED				= BIT(13),			// Set when the entity is enabled
	FL_BLOCKED				= BIT(14),			// Set when blocked by another entity
	FL_TELEPORT				= BIT(15),			// Toggled every time the origin abruptly changes
	FL_DAMAGEABLE			= BIT(16),			// Can take damage
	FL_INTERACTIVE			= BIT(17)			// Can be interacted with by pressing the "use" button
};

enum damageFlags_t {
	DAMAGE_RADIUS			= BIT(0),			// Damage was indirect (from a nearby explosion)
	DAMAGE_NO_ARMOR			= BIT(1),			// Armor does not protect from this damage
	DAMAGE_NO_KNOCKBACK		= BIT(2),			// Do not affect velocity, just view angles
	DAMAGE_NO_PROTECTION	= BIT(3)			// Kills godmode, armor, everything
};

enum moverType_t {
	MOVER_NONE,
	MOVER_BINARY,
	MOVER_PERIODIC,
	MOVER_COMPLEX
};

enum moverState_t {
	MOVER_POS1,
	MOVER_POS2,
	MOVER_1TO2,
	MOVER_2TO1
};

enum moveType_t {
	MOVE_NONE,
	MOVE_WATER,
	MOVE_AIR,
	MOVE_GROUND
};

enum projectileType_t {
	PT_SHELL,
	PT_SPIKE,
	PT_GRENADE,
	PT_ROCKET,
	PT_LIGHTNING,
	PT_GIB,
	PT_BRASS
};

class etEntity {
public:
	bool					inUse;
	bool					deferredFree;		// If true, it will be freed at a safe time

	const char *			name;
	const char *			className;

	int						flags;

	etDict					spawnDict;			// Dictionary of key/value pairs, used for entity spawning
	int						spawnFlags;

	entityState_t			state;

	int						sort;				// For sorting active entities

	int						snapshotCount;		// For avoiding duplicates in snapshots

	etEntity *				nextHash;

	// Current position and orientation
	glqVec3					currentOrigin;
	glqAngles				currentAngles;
	glqMat3					currentAxis;

	// Animator
	animator_t *			animator;

	// Clipping information
	bool					isClipping;

	int						clipMask;			// Surfaces with these contents will be collided against when moving

	clipModel_t *			clipModel;			// If NULL, collide against the bounding box

	bool					isDynamic;			// If true, the clip model must be reconstructed every frame

	int						contents;			// All contents OR'ed together

	glqBounds				bounds;				// Bounding box
	glqBounds				absBounds;			// Transformed and expanded for epsilons

	// Linking information
	bool					isLinked;

	struct clipSector_t *	clipSector;
	etEntity *				nextInClipSector;

	// Sounds
	float					audibleRange;		// For culling sounds by distance

	int						soundShader;

	int						soundIdle[3];
	int						soundPain[2];
	int						soundKill;
	int						soundGibKill;

	// Movers
	moverType_t				moverType;
	moverState_t			moverState;

	glqVec3					moverOrigin1;
	glqVec3					moverOrigin2;

	glqAngles				moverAngles1;
	glqAngles				moverAngles2;

	// General information
	etClient *				client;				// NULL if not a client

	glqPhysicsRigidBody *	physics;			// If not NULL, it can be pushed and fall off edges

	int						singleClient;		// For EF_SINGLECLIENT and EF_NOTSINGLECLIENT

	int						time;
	int						duration;

	float					wait;
	float					random;

	int						health;
	int						gibHealth;
	int						damage;

	moveType_t				moveType;

	float					speed;

	glqVec4					baseColor;

	float *					materialParms;		// Either NULL or a pointer to the material parms inside the state buffer
	float *					soundParms;			// Either NULL or a pointer to the sound parms inside the state buffer

	int						nextThinkTime;		// Run thinking code when level.time >= nextThinkTime
	int						nextTriggerTime;	// Allow retriggering when level.time >= nextTriggerTime

	etEntity *				owner;
	etEntity *				activator;

	etEntity *				groundEntity;		// NULL == in air

	etEntity *				moveMaster;			// Master of the mover team
	etEntity *				moveChain;			// Next entity in mover team

	etEntity *				teamMaster;			// Master of the physics team
	etEntity *				teamChain;			// Next entity in physics team

	// Callback functions
	void					(*Think)(etEntity *self);
	void					(*Shown)(etEntity *self);
	void					(*Hidden)(etEntity *self);
	void					(*Enabled)(etEntity *self);
	void					(*Disabled)(etEntity *self);
	void					(*Trigger)(etEntity *self, etEntity *other, etEntity *activator);
	void					(*Touch)(etEntity *self, etEntity *other, const trace_t *trace);
	void					(*Pain)(etEntity *self, etEntity *inflictor, etEntity *attacker, const glqVec3 &point, const glqVec3 &direction, int damage);
	void					(*Kill)(etEntity *self, etEntity *inflictor, etEntity *attacker, const glqVec3 &point, const glqVec3 &direction, int damage, meansOfDeath_t meansOfDeath);

private:
	bool					BindToMaster (etEntity *master, bool orientated);

	void					ParseMaterialParms (float *parms);
	void					ParseSoundParms (float *parms);

	bool					EvaluatePhysics ();

public:
	void					Setup (int number, int type, const char *name, const char *className, const etDict *dict);
	void					Clear ();

	int						Allocated () const;

	void					SetName (const char *newName);
	void					SetClassName (const char *newClassName);

	void					ParseGeneralState ();
	void					ParseMoverState ();
	void					ParseLightState ();
	void					ParseSpeakerState ();
	void					ParsePlayerState ();
	void					ParseItemState ();
	void					ParseMonsterState ();

	bool					IsBound () const;
	bool					IsBoundTo (etEntity *master) const;

	etEntity *				GetBindMaster () const;
	bool					GetBindOrientated () const;

	void					Bind ();
	void					Bind (etEntity *master, bool orientated);
	void					Unbind ();

	void					JoinTeam (etEntity *teamMember);
	void					QuitTeam ();

	void					JoinMoverTeam (etEntity *master);
	void					QuitMoverTeam ();

	void					SetMaterialParm (int index, float value);

	void					SetOrigin (const glqVec3 &origin);
	void					SetOriginInterpolate (const glqVec3 &origin);

	void					SetAngles (const glqAngles &angles);
	void					SetAnglesInterpolate (const glqAngles &angles);

	glqVec3					GetOrigin () const;

	glqAngles				GetAngles () const;

	void					SetClipModel (clipModel_t *clipModel, int contents, const glqBounds &bounds);

	void					EnablePhysics ();
	void					DisablePhysics ();

	void					RunPhysics ();

	void					RunThink ();

	void					UpdateAnimation ();

	bool					IsHidden () const;
	bool					IsEnabled () const;

	void					Hide ();
	void					Show ();

	void					Enable ();

	void					SetSort ();

	void					AddEvent (int eventType, int eventParm);

	void					Teleport (const glqVec3 &origin, const glqAngles &angles, bool keepMomentum = false);

	bool					CanDamage ();
	void					Damage (etEntity *inflictor, etEntity *attacker, const glqVec3 &point, const glqVec3 &direction, int damage, int damageFlags, meansOfDeath_t meansOfDeath);
};

/*
 ==============================================================================

 GLOBALS

 ==============================================================================
*/

#define MAX_IP_FILTERS					1024

#define MAX_SPAWN_POINTS				128		// FIXME: not sure if this is correct

#define ENTITIES_HASH_SIZE				(MAX_ENTITIES >> 2)

enum gameState_t {
	GS_DEAD,									// No level loaded
	GS_LOADING,									// Loading level and spawning entities
	GS_ACTIVE									// Normal gameplay
};

enum gameMode_t {
	GM_SINGLEPLAYER,
	GM_DEATHMATCH,
	GM_COOP
};

struct spawnPoint_t {
	glqVec3					origin;
	glqAngles				angles;
};

struct levelLocals_t {
	gameState_t				state;

	gameMode_t				mode;

	char					name[MAX_PATH_LENGTH];

	int						time;

	bool					restarting;

	bool					allowFrees;

	int						snapshotCount;

	const char *			ipFilters[MAX_IP_FILTERS];

	// Spawn points for players
	int						numSpawnPoints;
	spawnPoint_t			spawnPoints[MAX_SPAWN_POINTS];

	// Clients
	etClient *				clients;

	etClient *				localClient;

	// Entities
	int						numEntities;
	etEntity *				entities;

	int						numActiveEntities;
	etEntity *				activeEntities[MAX_ENTITIES];

	etEntity *				worldEntity;

	etEntity *				entitiesHashTable[ENTITIES_HASH_SIZE];

	// Game state
	const char *			configStrings[MAX_CONFIG_STRINGS];

	entityState_t			baselines[MAX_ENTITIES];
};

extern levelLocals_t		level;

extern glqServer_t *		glqServer;

extern cvar_t *				sg_showEntities;
extern cvar_t *				sg_maxShowDistance;
extern cvar_t *				sg_debugMove;
extern cvar_t *				sg_gravity;
extern cvar_t *				sg_filterBan;
extern cvar_t *				sg_gameMode;

void						SG_ParseBanIPs ();
bool						SG_FilterIP (const char *ip);

void						SG_AddCommands ();
void						SG_RemoveCommands ();

void						SG_DrawDebugTools ();

void						SG_RefreshLightEditor ();
void						SG_RefreshSpeakerEditor ();

void						SG_CloseLightEditor ();
void						SG_CloseSpeakerEditor ();

void						SG_InitLightEditor ();
void						SG_ShutdownLightEditor ();

void						SG_InitSpeakerEditor ();
void						SG_ShutdownSpeakerEditor ();

const char *				SG_ValidateEntityName (const char *name, const char *className, int entityNum);

void						SG_AddEntityToHashTable (etEntity *entity);
void						SG_RemoveEntityFromHashTable (etEntity *entity);

etEntity *					SG_FindEntity (const char *name);

etEntity *					SG_SpawnEntity (const etDict &dict);
void						SG_SpawnEntities (const char *entityString);
void						SG_SpawnLightAndSpeakerEntities ();

void						SG_SetConfigString (int index, const char *string);

int							SG_ConfigStringIndex (const char *string, int start, int max);

int							SG_ModelIndex (const char *name);
int							SG_MaterialIndex (const char *name);
int							SG_SoundShaderIndex (const char *name);

void						SG_WriteGameState (msg_t *msg, int offset);

void						SG_WriteDeltaPlayerState (msg_t *msg, playerState_t *oldState, playerState_t *newState);
void						SG_WriteDeltaEntityState (msg_t *msg, entityState_t *oldState, entityState_t *newState, bool force);

void						SG_WriteSnapshotToClient (etClient *client, msg_t *msg, int sequence, int deltaMessage, bool rateDelayed);

etEntity *					SG_AllocEntity (const char *name, const char *className, const etDict *dict);
void						SG_FreeEntity (etEntity *entity);

void						SG_ActuallyFreeEntities ();

void						SG_AngleToDir (float angle, glqVec3 &dir);

void						SG_KillBox (etEntity *entity);
void						SG_RadiusDamage ();

void						SG_CreateClipSectors ();

void						SG_InitClipSectors ();
void						SG_ShutdownClipSectors ();

void						SG_LinkEntity (etEntity *entity);
void						SG_UnlinkEntity (etEntity *entity);

void						SG_FireProjectile (projectileType_t type, etEntity *self, int damage);


#endif	// __SG_LOCAL_H__